home *** CD-ROM | disk | FTP | other *** search
Text File | 1993-08-24 | 62.4 KB | 1,958 lines |
-
-
- Animation Construction Kit 3D
- ACK-3D
- Lary Myers
-
-
- Contents:
- 1 ........... Purpose
- 2 ........... Using the ACK engine (DISCLAIMER)
- 3 ........... Hardware Requirements
- 4 ........... Development Environment
- 5 ........... Multi-User
- 6 ........... The Interface Structure
- 7 ........... Important fields in the Interface Structure
- 8 ........... Array Formats in the Interface Structure
- 9 ........... Initializing the ACK-3D engine
- 10 ........... ACK-3D Bitmaps
- 11 ........... Loading Bitmaps
- 12 ........... Creating Objects
- 13 ........... Using Overlays
- 14 ........... Moving around in ACK-3D
- 15 ........... Moving objects
- 16 ........... Normal and Secret Doors
- 17 ........... Sound and ACK-3D
- 18 ........... Function summary
- 19 ........... Where to Begin (EXAMPLES)
- 20 ........... Closing Comments
-
-
-
-
- (1) Purpose:
-
- ACK-3D began as an experiment to emulate the 3D effects seen in games like
- Wolfenstein-3D and Ken's Labyrinth and to make the technique available to the
- general public. So far things have worked out very well and in some areas have
- even exceeded the before mentioned games. With this latest installment ACK-3D
- gives the developer an easy to use library that can be linked into thier own
- applications without having to delve into the inner workings of the ray-casting
- engine. My hope is that others will use the ACK-3D engine to produce a new
- generation of 3D games that all of us can enjoy.
-
- Please forgive any glaring mistakes in this documentation. I find that I can
- write code all day long without getting weary, but as soon as I have to write
- documentation, forget it, too much like work! I hope the text below gives you
- enough information to use the engine.
-
-
-
- (2) Using the ACK-3D engine: (DISCLAIMER)
-
- The ACK-3D engine is being released as "PublicWare" and can be freely used
- in any private or commercial programs. The only restriction I would impose is
- that anything developed with the engine have its own disclaimer about liability
- and that under no circumstances will the author (me) be liable for any damages
- of any kind that may result from the use of the engine. "Basically the engine
- is being released "AS-IS" so it's left up to the developer to use it in a
- professional manner".
-
- If you develope a wonderful new game using the engine, I'd appreciate a
- mention in the documentation or credits screen, but only if you feel the ACK
- engine helped the game. Thanks.
-
-
- (3) Hardware Requirements:
-
- The current version of ACK-3D requires a minimum of a 386 33Mhz machine and
- will not run on a 286 or below because of 386 code being used. A coprocessor
- is NOT required or needed for the engine itself. Any other hardware requirements
- are left up to the applications that use the engine (ie. mouse, VGA, etc).
- A VGA is only required if the ACK display routines are used (which is up to the
- application). If the application chooses to use another display mode then it
- should issue the appropriate hardware requirements.
-
-
-
-
- (4) Development Environment:
-
- ACK-3D was developed using BorlandC Version 3.1 in compact model. It is
- setup to also use large model (both may be supplied). The assembly routines
- were compiled using the Microsoft Assembler (MASM) version 5.00A and should
- work with later versions of MASM. The library was created using the Microsoft
- Library Manager (LIB) version 3.02 and should also work with later versions
- of LIB.
-
- All of the ACK code is written in either normal C or assembler.
-
- Compiler switches used with Borland;
-
- -ml = Large model
-
- -c = Compile only, don't link
-
- -3 = Use 386 instructions
-
- -O2 = Compile for size
-
- -G = Compile for speed
-
- -I = specifies the include path for header files
-
- -o = specifies the object path and file name
-
-
- Assembler switches used with MASM;
-
- /Ml = Case sensitive for all symbols and labels
-
- /B63 = Size of buffer during assembly (does not affect OBJ file)
-
- /D_ML
- /D_MC = Assembly directive for large (_ML) or compact (_MC) model.
- Used by ET.MAC for various macros.
-
-
- Directory structure;
-
- I use the following subdirectories for the ACK engine and demo. This
- is provided for your information. It is what the .MAK files are setup for.
-
- Engine source;
-
- \borlandc\ack3d\eng <- .C .H .MAK files
-
- \borlandc\ack3d\eng\cmobj <- Compact model .OBJ and .LIB files
-
- \borlandc\ack3d\eng\lmobj <- Large model .OBJ and .LIB files
-
-
- Demo source;
-
- \borlandc\ack3d\demo <- .C .H .BAT .MAK .EXE .DAT files
-
- \borlandc\ack3d\demo\bitmaps <- .BBM and .LBM files
-
- \borlandc\ack3d\demo\sound <- .VOC .CMF .PWM files
-
-
-
-
-
- (5) Multi-User:
-
- ACK-3D uses an interface structure to communicate between the application
- and the engine. This structure (which will be explained later) contains the
- pertinent data that the engine needs to draw the current Point of View (POV).
- It is possible to do some fancy things with this approach. Here are some of
- the things I've tried;
-
- 1. Displaying more than one view at the same time such as directly in
- front (the normal view) and directly behind (essentially eyes in the
- back of your head!).
-
- 2. Displaying more than one player on the map is also possible but has
- some restrictions that may make it unusable. These restrictions may
- be lifted in the near future.
-
-
- Some discussion has been going on for a network version of ACK-3D. While
- this is not built into the engine for this version, it does seem to be a
- straightforward progression from this point. Future versions of the engine
- may indeed support multiple players logging into a 3D world and interacting
- with each other in real-time!
-
-
- (6) The Interface Structure:
-
- ACK-3D uses a structure from the application to get the necessary
- information for building a complete POV. It is the applications responsibility
- to correctly create and setup this structure before the ACK engine is called.
- Limited bounds checking is in the engine to prevent degradation of speed during
- the build process. UNPREDICTABLE RESULTS MAY OCCUR WITH INVALID DATA!
-
- The structure is defined in the header file ACK3D.H and can be either in
- the data segment of the calling application or allocated in memory whichever
- the application desires. Within the structure are variables for things like the
- current location and angle of the player, the actual map arrays for the walls
- and objects, pointers to the screen buffer, overlay buffer and background
- buffer, as well as dimensions of the viewport to build the walls into. The
- term "build" is being used here because the ACK engine will not display to the
- screen unless told to do so in a separate function. This way the application
- is free to display the current POV anyway it wants to, or it can use the
- supplied function to display if it doesn't. This offers maximum flexibility in
- how the engine is used.
-
-
-
- (7) Important fields in the Interface Structure:
-
- Before calling the ACK engine to initialize, some very important items
- need to be filled into the interface structure. These are described below;
-
- WinStartX <- The leftmost pixel coordinate of the viewport
- WinEndX <- The rightmost pixel coordinate of the viewport
- WinStartY <- The upper pixel coordinate of the viewport
- WinEndY <- The lower pixel coordinate of the viewport
-
- These four fields will allow the ACK engine to fill in some other fields
- within the interface structure, such as WinWidth, WinHeight, etc.
-
- Before calling the function to construct the background, the following
- fields must be filled in;
-
- TopColor <- Color value of the ceiling
- BottomColor <- Color value of the floor
- LightFlag <- Whether light shading is on or off
-
- Before calling the function to actually build the POV, the following fields
- must be filled in;
-
- xPlayer <- X coordinate for the POV
- yPlayer <- Y coordinate for the POV
- PlayerAngle <- POV angle in ACK units
-
-
- The ACK engine relies on the POV fields above to build the current scene.
- By changing these values it's possible to build a variety of scenes one after
- the other. For example, changing the PlayerAngle to be 180 degrees from the
- current angle and then calling the build routine, will create a scene that is
- directly behind the current POV.
-
-
-
- (8) Array Formats in the Interface Structure:
-
- Several arrays are used to build the 3D POV scene. ACK-3D uses a map layed
- out in squares like graph paper to determine what the POV sees at any given
- time. This map is a 2 dimensional array of 64 columns by 64 rows. When the
- initialize routine reads in this map, it processes the data and builds the
- arrays in the interface structure. These arrays are xGrid and yGrid which are
- used for drawing the walls. The layout of these arrays is different from the
- map file and is described below;
-
-
- 1,1,1,1,1,1,1,1 Example map of size 8x8, the real map
- 1,0,0,0,0,0,0,1 would be the same except 64x64
- 1,0,0,0,0,0,0,1
- 1,0,1,1,1,0,0,1
- 1,0,1,0,1,0,0,1
- 1,0,1,1,1,0,0,1
- 1,0,0,0,0,0,0,1
- 1,1,1,1,1,1,1,1
-
-
-
- This is an example of a map where 0 is a blank square and non-zero values
- represent wall bitmap numbers (from 1 to 255). The ACK engine requires this
- same information broken down into walls that fall on X planes and walls that
- fall on Y planes, so the array above is changed into;
-
- 1,1,1,1,1,1,1,1,1
- 1,0,0,0,0,0,0,1,1 This would be a map of X walls, note the extra
- 1,0,0,0,0,0,0,1,1 column on the right side of the map.
- 1,0,1,1,1,0,0,1,1
- 1,0,1,0,1,0,0,1,1
- 1,0,1,1,1,0,0,1,1
- 1,0,0,0,0,0,0,1,1
- 1,1,1,1,1,1,1,1,1
-
-
-
- 1,1,1,1,1,1,1,1
- 1,0,0,0,0,0,0,1 This would be a map of Y walls, note the extra
- 1,0,0,0,0,0,0,1 row on the bottom of the map.
- 1,0,1,1,1,0,0,1
- 1,0,1,0,1,0,0,1
- 1,0,1,1,1,0,0,1
- 1,0,0,0,0,0,0,1
- 1,1,1,1,1,1,1,1
- 1,1,1,1,1,1,1,1
-
-
- This is basically what becomes of the original map in order for the engine
- to see walls in either the X or Y planes. What this means is that anytime the
- map needs to be looked at, the following applies;
-
-
- int MapPosn; /* Location to look at in map */
-
-
- xGrid[MapPosn]; /* This would be the left X walls */
- xGrid[MapPosn + 1]; /* This would be the right X wall */
-
- yGrid[MapPosn]; /* This would be the top Y wall */
- yGrid[MapPosn + GRID_WIDTH]; /* This would be the bottom wall */
-
-
- If all four of these locations is 0 then there is nothing at this square in
- the map. What does this mean? One thing that would be possible (but not part of
- the ACK engine), is that walls would not have to be cubes, but could be single
- wall panels (might look alittle funny from the side since they wouldn't have
- any width to them). It could also mean that different bitmaps could be used
- for various sides of the cube.
-
-
- The next two arrays are bMaps and oMaps which, for now, are simple arrays
- that point to the wall and object bitmaps respectively. Each bitmap takes
- 4096 bytes of memory for the 64x64 size bitmap. BUT, the important point is
- that ACK reads these bitmaps in normal row order, where there is a row of
- color bytes one after the other, like so;
-
- 1,2,3,... An arbitrary bitmap of 64x64
- 1,1,1,...
- 4,2,4,...
- ....
-
- This bitmap is then "rotated" 90 degrees so it is in column order, which
- makes it easier to use when drawing the bitmap, thus the above becomes;
-
- 1,1,4,.... Bitmap rotated 90 degrees
- 2,1,2,....
- 3,1,4,....
-
- -----------------------------------------
- Another important array in the interface structure is the PalTable[] array
- which controls how the ACK engine will perform light shading. The array contains
- 16 ranges of 256 colors each which are used to substitute for the actual colors
- of the bitmap based on the distance away from the POV.
-
- The distance to the wall or object is first divided by 64 to get the zone
- to use for shading (zones greater than 15 are set at 15). This zone of 256 is
- then used as a lookup table indexed by the color of the bitmap. What all this
- allows is a gradual darkening of walls and objects as they become farther away
- from the POV while still allowing some colors to be used as constant lighting
- colors (they will never darken with distance).
-
-
- -----------------------------------------
-
- Now we come to doors. Within the interface structure there is a sub-
- structure labeled Door[MAX_DOORS]. This structure array holds the current
- status of all doors that may be in the process of opening or closing. This
- array is NOT all the doors in the map, only those that are in motion. If the
- ColOffset field of the array is non-zero the door is active and either opening
- or closing. When an application makes a call to AckCheckDoorOpen() the engine
- will determine if the POV is close enough to a door and initialize it in the
- Door[] array. After that, every call to AckBuildView() will cause the door to
- be updated until it goes fully open then fully closed. During this time the
- application is free to alter the fields in the Door[] array (BEWARE that mPos
- and mPos1 are map positions and should NOT be altered during this time). If,
- for example, the application wants a door to remain open for a long period of
- time, it can check the value of ColOffset to see where a door currently is. If
- the value is less than 64 the door is partially open (or partially closed).
- Once the value becomes greater than 64 the door is no longer visible and is
- fully open. By keeping the value greater than 64 (setting the Speed field to
- zero) the door will no longer be moved. Another way might be to set the field
- ColOffset to zero and not allow the door to be seen anymore. This method would
- be the best if the application desires the door to remain permanently open.
- Here is a breakdown of the DOORS structure;
-
-
- mPos <- Map position for one side of cube
-
- mPos1 <- Map position for other side of cube
-
- mCode <- Bitmap code that represents the door
-
- mCode1 <- Bitmap code for door on other side
-
- ColOffset <- Current column offset of door (see above)
-
- Speed <- Speed is added to ColOffset during open/close
-
- Type <- Type of door (ie DOOR_XCODE or DOOR_YCODE)
-
- Flags <- Current door action (DOOR_OPENING or DOOR_CLOSING)
-
- The current version of the ACK engine uses the following wall bitmap values
- for doors;
-
- 60 <- Door lies on the X plane (vertical)
-
- 61 <- Side panel that appears on both sides of door
-
- 62 <- Door lies on the Y plane (horizontal)
-
-
- -----------------------------------------
-
- Okay, now let's look at the OBJECT sub-structure. Again, this is an array
- stored within the interface structure and accessable to the application as well
- as the ACK engine. Every object in the map is represented by an entry in this
- array. The AckInitialize() function automatically fills in the initial X and Y
- coordinates of the object when it finds one in the map file. The rest of the
- object data must be filled in by the application before the objects can be
- used. The section below on Creating Objects describes what fields should be
- setup by the application.
-
- Some of the other fields in the OBJECT array are described below;
-
- Active <- 0 or 1 to indicate the object should be considered during
- the AckCheckObjectMovement() function.
-
- bmNum <- Holds from 1 to MAX_VIEW bitmap indices to display for
- the object. If more than 1 bitmap the object will either
- animate in place or display multiple views as the POV
- walks around the object.
-
- Sides <- Calculated by AckCreateObject() function dependent on
- the number of bitmaps in bmNum.
-
- Dir <- Which direction the object will move.
-
- Flags <- This field can contain OF_PASSABLE which allows the POV
- to walk right through the object. (Good for overhead
- lights and such). It can also contain OF_ANIMATE, which
- is used by the function AckCheckObjectMovement() to
- cycle between multiple bitmap images for the object.
-
- CurNum <- Current index (base 0) of the bitmap to display out of
- bmNum. If the object only has 1 bitmap then CurNum will
- always be 0.
-
- MaxNum <- Total number of bitmaps (base 0) contained in bmNum. If
- the object only has 1 bitmap then MaxNum will be 1.
-
- Speed <- The speed at which the object will move. Normal values
- are from 2 to 40, higher than this causes alot of
- jmuping and may cause the object to pass through walls.
-
- VidRow <- Reserved.
-
- x and y <- Map coordinates of the object, from 0 to 4095.
-
- mPos <- The actual map grid location of the object. If x and y
- are changed by the application then mPos should also
- be updated by using the equation;
-
- mPos = (y & 0xFFC0) + (x >> 6);
-
-
- The Dir field is a left-over from the older version of the engine, where
- it was used for crude 8 direction movement, or rotating in place. With this
- latest version, the OF_ANIMATE flag is used to indicate cycling through the
- bitmaps for the object and the Dir field is ignored. The application can
- still use this field for storing an angle of movement for use with the
- function AckMoveObjectPOV().
-
-
-
-
- (9) Initializing the ACK-3D engine:
-
- Before anything can be done with the ACK engine, it must be initialized.
- This is done by creating the interface structure and passing it into the
- AckInitialize() function. The following example shows one method to accomplish
- this;
-
-
- ACKENG ae; /* Interface structure in global memory */
-
- int main()
- {
- int result;
-
-
- ae.WinStartY = 0; /* Setup viewport coordinates */
- ae.WinEndY = 104;
- ae.WinStartX = 0;
- ae.WinEndX = 319;
-
- result = AckInitialize(&ae); /* Initialize the engine */
-
- if (result)
- {
- printf("Error initializing - Code: %d\n",result);
- exit(1);
- }
-
- result = AckReadMapFile(&ae,"DEMOMAP.L01"); /* Read map file */
-
- if (result)
- {
- printf("Error reading map - Code: %d\n",result);
- exit(1);
- }
-
- }
- Example 1
-
-
- The function AckInitialize() will return an error code if there was a
- problem initializing the engine or a zero if successful. At this point the
- ACKENG interface structure is initialized, the file "TRIG.DAT" has been
- read into the various tables that ACK-3D requires.
-
- No bitmaps have been dealt with yet so there is still more to do before
- actually drawing the first POV.
-
- No objects have been dealt with yet. The application must handle these
- either on its own or by using one of the supplied ACK-3D functions.
-
-
-
-
- (10) ACK-3D Bitmaps:
-
- All bitmaps used by ACK-3D are 64 pixels wide by 64 pixels tall and are
- stored in normal line by line fashion (ie. One row of 64 pixels then another
- row, and so forth). Each byte represents one pixel and may have a color from
- 0 to 255. The engine itself requires the bitmaps to be in raw image form,
- however the function AckLoadBitmap() will accept either raw IMG format or
- Deluxe Paint II brush files (.BBM extensions).
-
-
- (11) Loading Bitmaps:
-
- ACK-3D provides several functions to load in bitmaps. These functions don't
- have to be used and are only provided for convienence to the application. They
- are as follows;
-
- NOTE: If the application chooses to load it's own bitmaps it MUST be sure
- to rotate the bitmaps 90 degrees into the column/row order needed
- by the engine. Also, the function below uses Extended memory (XMS)
- if available which the application may or may not decide to support.
-
-
- int AckLoadBitmap(ACKENG *ae,int BitmapNumber,int BitmapType,
- char *bmFileName);
-
- where:
- ae <- Interface structure
- BitmapNumber <- A value from 1 to 255
- BitmapType <- Either TYPE_WALL or TYPE_OBJECT
- bmFileName <- Name of bitmap file to read
-
- This is the general purpose bitmap load routine. It will read either
- raw image files (.IMG) or Deluxe Paint brush files (.BBM) and place the
- bitmap into the appropriate bitmap array (bMaps for walls and oMaps for
- objects).
-
-
- int AckLoadWall(ACKENG *ae,int WallNumber,char *bmFileName);
-
- where:
- ae <- Interface structure
- WallNumber <- Value from 1 to 255
- bmFileName <- Name of bitmap file to read
-
- This routine simply calls AckLoadBitmap() with TYPE_WALL set.
-
-
- int AckLoadObject(ACKENG *ae,int BmpNumber,char *bmFileName);
-
- where:
- ae <- Interface structure
- BmpNumber <- Value from 1 to 255
- bmFileName <- Name of bitmap file to read
-
- This routine simply calls AckLoadBitmap() with TYPE_OBJECT set.
-
-
-
-
- (12) Creating Objects:
-
- Objects in ACK-3D can be either stationary or movable depending on the needs
- of the application. Both types of object are handled exactly the same, the
- stationary ones just never change location. Objects can share bitmaps if needed
- and can have more than one bitmap to accomplish different tasks. If the object
- has multiple bitmaps it can either stay in one spot and display the bitmaps
- in sequence (this animates the object) or can use the mulitple bitmaps to show
- different views of the object when the POV is walking around it.
-
- The sequence for creating an object is as follows;
-
- 1) Load any bitmaps associated with the object.
-
- 2) Specify the object speed and any flags such as OF_PASSABLE or OF_ANIMATE.
-
- 3) Call the routine AckCreateObject() to fill-in other information in the
- object structure.
-
- Example;
-
- int main()
- {
-
- /* Initialization done per Example 1 above */
-
- AckLoadObject(&ae,1,"object1.bbm"); /* Load in a bitmap for object */
-
- ae.ObjList[1].Flags |= OF_ANIMATE; /* Stay in place and animate */
- ae.ObjList[1].Speed = 1; /* Speed is non-zero to activate */
- AckCreateObject(&ae,1,1,nums);
-
-
- }
-
- Example 2
-
-
- The example above uses an array of unsigned chars called nums[] to specify
- the different views of the object. In this case there is only one so the number
- of views is also passed as 1. There can be as many as MAX_VIEWS (ack3d.h)
- different bitmaps assigned to one object for use as animation, etc.
-
-
-
- (13) Using Overlays:
-
- If the application so desires, an overlay screen can be used with ACK-3D.
- This overlay will only be effective if the AckDisplayScreen() function is used,
- unless the application manipulates the overlay on its own.
-
- What an overlay does is allow a full screen picture to be used which may
- overlay some of the area where walls are displayed. There could be ancient
- pillars, or merely a sign that says "Demo in progress" or whatever, the whole
- point to the overlay is to provide a means of displaying graphics over the top
- of the viewport when displayed on the screen.
-
- The overlay is read in like a normal bitmap, except it is full-screen in
- size (320x200). It is then compiled into drawing commands that are placed in
- the interface structure pointed to by OverlayBuffer. Everytime the function
- AckDisplayScreen() is called, this pointer is used to draw the overlay on top
- of the last POV that was built.
-
- Overlays are optional and do not need to be used unless desired by the
- application. For those who wish to process the compiled overlay themselves,
- here is the format for the OverlayBuffer;
-
-
- Length 2 bytes <- Length of data below (does not include offset)
- Offset 2 bytes <- Offset into screen to show data
- Data n bytes <- Actual data of size Length
-
- Length,Offset,Data combinations will continue until a Length of zero
- is reached, meaning no more data.
-
- The function AckDrawOverlay(ScreenBuffer,OverlayBuffer) can be used to
- place the overlay on top of any walls that were drawn. This function processes
- the overlay commands described above and draws into the screen buffer. The
- application can then do any additional drawing it desires before actually
- displaying to the video.
-
-
- (14) Moving around in ACK-3D:
-
- Once things are initialized and the current POV is built and displayed, it
- becomes time to move around in the map. This is accomplished by using the
- function:
-
- int AckMovePOV(ACKENG *ae,int Angle,int Amount);
-
- where:
- ae <- Interface structure
- Angle <- Direction to move
- Amount <- Amount to move
-
-
- In its simplest form the function can just be called with the current angle
- the POV is facing and some amount to move, such as;
-
- AckMovePOV(&ae,ae.PlayerAngle,16);
-
- But it can also be used to backup, with the following;
-
- NewAngle = ae.PlayerAngle + INT_ANGLE_180;
-
- if (NewAngle >= INT_ANGLE_360)
- NewAngle -= INT_ANGLE_360;
-
- AckMovePOV(&ae,NewAngle,16);
-
- This function does the necessary collision detection and returns 0 if the
- POV actually moved, in which case the values ae.xPlayer and ae.yPlayer have
- been updated with the new coordinates of the POV.
-
- If the application wishes to do the moving itself, it can make a call to
- the function AckCheckHit() which returns 0 if no walls are in the way. Any
- collisions with objects will not be returned from AckCheckHit.
-
-
-
- (15) Moving Objects:
-
- ACK-3D contains two functions for handling object animation and movement.
- Animation is performed by switching the displayed bitmap for the object
- whenever the function is called. The application is responsible for setting
- up the object structure to provide the engine with the necessary information
- to animate or move the object.
-
- void AckCheckObjectMovement(ACKENG *ae);
-
- This function will check all active objects and determine if any need
- to have thier bitmaps changed for animation. The application can perform this
- check itself if so desired. This function is provided for convienence.
-
-
- int AckMoveObjectPOV(ACKENG *ae,int ObjIndex,int Angle,int Amount);
-
- This function actually moves the object at the specified Angle for the
- specified Amount. The return value can be processed by the application to
- determine if the object has struck a wall, another object, or the player.
-
-
-
- (16) Normal and Secret Doors:
-
- Once ACK-3D reads in the map file, it is processed for a variety of things,
- one of these being where doors will appear in the map. The application should
- establish where doors will appear within the map file BEFORE calling the
- function AckInitialize(). Doors are treated as special walls in the ACK engine
- and will not automatically be checked to see if they should open. This is up
- to the application and can be accomplished with the function;
-
-
- void AckCheckDoorOpen(int xPlayer,int yPlayer,int PlayerAngle,ACKENG *ae);
-
- where:
- xPlayer <- Current x coordinate of the POV
- yPlayer <- Current y coordinate of the POV
- PlayerAngle <- Current angle the POV is facing
- ae <- Pointer to the interface structure
-
- What this function does is determine if the POV is close enough to either
- a normal or secret door and set the appropriate information to begin opening
- the door. This information is kept in the Door[] array of the interface
- structure and is then used by the ACK engine during subsequent builds. After
- the door has been triggered to open, the process is automatic (unless the
- application manipulates the Door array, which it can if need be) until the
- door goes back to fully closed.
-
-
- (17) Sound and ACK-3D:
-
- The ACK engine is being released with a public domain program from Mystic
- software that allows playing of SoundBlaster .CMF and .VOC files. In addition
- the files with .PWM extensions are capable of playing through the PC speaker.
-
- Since the sound features are public domain there are some limitations that
- are imposed by Mystic Software. The first is that a Terminate and Stay Resident
- program (TSR) must be loaded before using the sound routines. The program is
- included with the ACK demo and is called WORXLITE.EXE. This TSR need be loaded
- only once when first running the demo program.
-
- The second limitation is how the sound is used. Sound files consume alot
- of memory so don't try to load several of them or large ones, they won't fit.
- There also appears to be some problems with the sound routines that need to
- be watched out for. On some machines it appears that playing background music
- with the .CMF files will occasionally lock up the machine and require a hard
- reboot to clear it. Playing the sound effect (.VOC files) alone seems to work
- fine. The other problem is that .VOC files cannot be loaded after background
- music is started playing, this forces all VOC files to be loaded up front which
- contributes to the memory overhead mentioned earlier.
-
- But, the sound routines are provided to allow you to experiment with them
- in the 3D engine without having to write your own. The full fledged WORX
- library can be purchased from Mystic Software if you wish to use it in a full
- fledged shareware or commercial application.
-
-
-
- (18) Function Summary:
-
- Here is a list of the available ACK-3D functions and what they do.
-
- ------------------------------------------------------------------------------
- int AckInitialize(ACKENG *ae);
- where:
- ae <- Pointer to interface structure
-
- Purpose:
- Initializes the various arrays used by the ACK engine.
- Reads and processes the file "TRIG.DAT".
- Reads and processes the Map file supplied in the call.
- Allocates 64000 bytes for the ScreenBuffer.
- Calculates viewport items based on initial dimensions setup by the
- application.
-
- Returns:
- 0 if successful
- One of the error codes listed in ACK3D.H
-
- Notes:
- This function must be called before any other ACK function.
-
-
-
- ------------------------------------------------------------------------------
- int AckReadMapFile(ACKENG *ae,char *MapFileName);
- where:
- ae <- Pointer to interface structure
- MapFileName <- Name of map/object file to read
-
- Purpose:
- Reads and processes the ACK binary map file.
-
- Returns:
- 0 if successful
- One of the error codes listed in ACK3D.H
-
- Notes:
- Upon return the xGrid and yGrid arrays will be filled in with the
- wall bitmap numbers. The application is then free to change these if it
- wishes some walls to be different.
-
-
- ------------------------------------------------------------------------------
- int AckLoadBitmap(ACKENG *ae,int BitmapNumber,int BitmapType,char *bmFileName);
- where:
- ae <- Pointer to interface structure
- BitmapNumber <- Index number into bitmap array
- BitmapType <- Either TYPE_WALL or TYPE_OBJECT
- bmFileName <- Name of bitmap file to read
-
- Purpose:
- Reads in either raw (IMG) or DPII (BBM) bitmap file and processes it
- into the form that ACK-3D requires.
- Allocates the 4K of memory for the bitmap and stores the pointer in
- either bMaps[] or oMaps[] array based on BitmapType.
-
- Returns:
- 0 if successful
- One of the error codes listed in ACK3D.H
-
- Notes:
- This function now uses XMS (Extended Memory) when possible to load
- bitmaps.
-
-
- ------------------------------------------------------------------------------
- int AckLoadWall(ACKENG *ae,int WallNumber,char *bmFileName);
- where:
- ae <- Pointer to interface structure
- WallNumber <- Index number into bitmap array
- bmFileName <- Name of bitmap file to read
-
- Purpose:
- Calls AckLoadBitmap() function with TYPE_WALL BitmapType set.
-
- Returns:
- 0 if successful
- One of the error codes listed in ACK3D.H
-
- Notes:
- This function now uses XMS (Extended Memory) when possible to load
- bitmaps.
-
-
- ------------------------------------------------------------------------------
- int AckLoadObject(ACKENG *ae,int BmpNumber,char *bmFileName);
- where:
- ae <- Pointer to interface structure
- BmpNumber <- Index number into bitmap array
- bmFileName <- Name of bitmap file to read
-
- Purpose:
- Calls AckLoadBitmap() function with TYPE_OBJECT BitmapType set.
-
- Returns:
- 0 if successful
- One of the error codes listed in ACK3D.H
-
- Notes:
- This function now uses XMS (Extended Memory) when possible to load
- bitmaps.
-
-
-
- ------------------------------------------------------------------------------
- int AckCreateObject(ACKENG *ae,int ObjNumber,int NumBitmaps,UCHAR *bmNums);
- where:
- ae <- Pointer to interface structure
- ObjNumber <- Index number into ObjList array (Different than bitmap
- number!).
- NumBitmaps <- Number of bitmap indexes contained in bmNums.
- bmNums <- List of bitmap numbers associated with this object.
-
- Purpose:
- Sets up ObjList structure with information regarding the object.
- Calculates the number of sides to the object if multiple bitmaps are
- specified.
-
- Returns:
- 0 if successful
- One of the error codes listed in ACK3D.H
-
- Notes:
- None
-
-
- ------------------------------------------------------------------------------
- int AckCreateOverlay(ACKENG *ae, UCHAR far *OverlayScreen);
- where:
- ae <- Pointer to interface structure
- OverlayScreen <- Pointer to 64k screen image to use.
-
- Purpose:
- Determines which part of the screen is within the viewport and compiles
- this area for use by the AckDisplayScreen() function. The compiled
- commands are placed in the pointer OverlayBuffer in the interface
- structure.
-
- Returns:
- 0 if successful
- One of the error codes listed in ACK3D.H
-
- Notes:
- This function process the overlay screen passed and builds a compiled
- overlay buffer (which is allocated) and returned in ae->OverlayBuffer.
-
-
- ------------------------------------------------------------------------------
- int AckBuildBackground(ACKENG *ae);
- where:
- ae <- Pointer to interface structure
-
- Purpose:
- Builds a static floor and ceiling background image based on the values
- of TopColor, BottomColor and LightFlag in the interface structure. The
- resulting image is pointed to by BkgdBuffer in the interface structure.
-
- Returns:
- 0 always
-
- Notes:
- This function builds a psuedo-shaded ceiling and floor picture that is
- used for the background of the screen. The application can override this
- and have its own background if desired.
-
-
- ------------------------------------------------------------------------------
- int AckBuildView(ACKENG *ae);
- where:
- ae <- Pointer to interface structure
-
- Purpose:
- Constructs the current POV and places the result into ScreenBuffer
- pointer in the interface structure. No displaying is done to the
- screen at this time, nor has the optional overlay buffer been processed
- by this function.
-
- Returns:
- 0 always
-
- Notes:
- The best place to put this function is in the applications main loop
- so it is repeatedly called whenever the POV moves or objects move or
- animate.
-
-
- ------------------------------------------------------------------------------
- void AckDrawOverlay(UCHAR far *Screen,UCHAR far *Overlay);
- where:
- Screen <- Buffer to draw overlay buffer into
- Overlay <- Pointer to compiled overlay image
-
- Purpose:
- Processes the compiled overlay image and draws into the screen buffer.
-
- Notes:
- This function should be called before AckDisplayScreen (or the
- applications own display function) if an overlay is used.
-
-
-
- ------------------------------------------------------------------------------
- int AckDisplayScreen(ACKENG *ae);
- where:
- ae <- Pointer to interface structure
-
- Purpose:
- Display the last built POV onto the screen in normal VGA mode 13h.
-
-
- Returns:
- 0 always
-
- Notes:
- If the application wishes to use an overlay it must call the function,
- AckDrawOverlay() before calling AckDisplayScreen().
-
-
- ------------------------------------------------------------------------------
- void AckCheckObjectMovement(ACKENG *ae);
- where:
- ae <- Pointer to interface structure
-
- Purpose:
- Scans the ObjList sub-structure of the interface structure to determine
- if any objects need to be updated. This function should be called
- before the AckBuildView() function.
-
- Returns:
- Nothing
-
- Notes:
- This routine is mainly good for animating objects that have multiple
- bitmaps. The application should devise its own movement algorithms and
- call AckMoveObjectPOV() to carry them out.
-
-
- ------------------------------------------------------------------------------
- int AckMovePOV(ACKENG *ae,int Angle,int Amount);
- where:
- ae <- Pointer to interface structure
- Angle <- Angle to move POV
- Amount <- Amount to move POV
-
- Purpose:
- Determines if the move is valid and then sets the new coordinates into
- xPlayer and yPlayer of the interface structure.
-
- Returns:
- 0 if successful
- 1 if X wall was hit
- 2 if Y wall was hit
- 3 if an object was hit
-
- Notes:
- None
-
-
-
-
- ------------------------------------------------------------------------------
- int AckCheckDoorOpen(int xPlayer,int yPlayer,int PlayerAngle,ACKENG *ae);
- where:
- xPlayer <- Current x coordinate of the POV
- yPlayer <- Current y coordinate of the POV
- PlayerAngle <- Current angle POV is facing
- ae <- Pointer to interface structure
-
- Purpose:
- Determines if the POV is close enough to trigger a door open. If so,
- the door is placed in the Door sub-structure of the interface structure
- and the opening process is begun. Subsequent calls to AckBuildView()
- will automatically continue the open and close process.
-
- Returns:
- 0 if no door was opened
- 1 if X door was opened
- 2 if Y door was opened
- 3 if X secret door was opened
- 4 if Y secret door was opened
-
- Notes:
- None
-
-
-
- ------------------------------------------------------------------------------
- int AckCheckHit(int xPlayer,int yPlayer,int ViewAngle,ACKENG *ae);
- where:
- xPlayer <- Current x coordinate of the POV
- yPlayer <- Current y coordinate of the POV
- ViewAngle <- Current angle POV is facing
- ae <- Pointer to interface structure
-
- Purpose:
- Allows the application to determine if an obstacle is close to the POV.
-
- Returns:
- 0 if nothing nearby
- 1 if X wall is close
- 2 if Y wall is close
-
- Notes:
- This function is called by AckMovePOV() and checks for collisions with
- walls (not objects).
-
- ------------------------------------------------------------------------------
- int AckGetObjectHit(void);
-
- Purpose:
- Allows the application to determine the object the POV last came in
- contact with.
-
- Returns:
- Returns the object index of the last object the POV hit.
-
- Notes:
- None
-
-
-
- ------------------------------------------------------------------------------
- int AckDeleteObject(ACKENG *ae,int ObjectIndex);
- where:
- ae <- Pointer to interface structure
- ObjectIndex <- Index number of object to delete
-
- Purpose:
- Essentially sets the objects Active flag to 0 so the object is no longer
- checked by the engine. Good idea to call this function for future
- versions which may need to do more processing.
-
- Returns:
- -1 if object already inactive
- 0 if object was deleted from map
-
- Notes:
- None
-
-
- ------------------------------------------------------------------------------
- void AckSetVGAmode(void);
-
- Purpose:
- Places the screen into standard 320x200 VGA mode 13h.
-
- Returns:
- Nothing
-
- Notes:
- This is a utility function. The application can set the screen to
- graphics using its own routines if it so desires, the ACK engine does not
- depend on mode 13h unless the AckDisplayScreen() function is called.
-
-
- ------------------------------------------------------------------------------
- int AckLoadAndSetPalette(char *FileName);
- where:
- FileName <- Name of palette file to load
-
- Purpose:
- Reads the specified palette file then sets the palette of the VGA.
-
- Returns:
- 0 if successful
- One of the error codes listed in ACK3D.H
-
- Notes:
- This is a utility function to read a palette file (768 bytes) and
- set the video palette to the contents of the file. The application can
- use it's own function if desired or can also use AckSetPalette() with
- a buffer if needed.
-
-
- ------------------------------------------------------------------------------
- void AckSetPalette(UCHAR far *PalBuffer);
- where:
- PalBuffer <- 768 byte buffer containing palette information
-
- Purpose:
- Sets the contents of the PalBuffer into the video palette.
-
- Returns:
- Nothing
-
- Notes:
- Use this function to set a palette that has already been read into
- a buffer. Use AckLoadAndSetPalette() to read AND set a palette from a
- file. This is a utility function.
-
-
- ------------------------------------------------------------------------------
- void AckFadeIn(int Begin,int Count,UCHAR far *Palette);
- where:
- Begin <- Starting color index to fade in
- Count <- Number of sequential color indexes to fade
- Palette <- Buffer containing palette information to use
-
- Purpose:
- Slowly fades the screen from black to the contents of the Palette
- buffer passed.
-
- Notes:
- This is a utility function.
-
-
- ------------------------------------------------------------------------------
- void AckFadeOut(int Begin,int Count);
- where:
- Begin <- Starting color index to fade in
- Count <- Number of sequential color indexes to fade
-
- Purpose:
- This function slowly fades the screen from the current palette to black.
-
- Notes:
- This is a utility function.
-
-
- ------------------------------------------------------------------------------
- void AckSetTextmode(void);
-
- Purpose:
- Places the screen into normal 80x25 text color mode 3.
-
- Returns:
- Nothing
-
- Notes:
- This is a utility function.
-
-
- ------------------------------------------------------------------------------
- UCHAR far *AckReadiff(char *FileName);
- where:
- FileName <- Name of .LBM or .BBM file to read
-
- Purpose:
- Reads in a Deluxe Paint picture (.LBM) or brush (.BBM) file, allocates
- a buffer for the images and returns the buffer pointer to the caller.
- The image in the buffer will contain 4 bytes at the beginning with the
- width and height of the image in integer format.
- This function is provided if the application wishes to read its own
- images and is also needed if the overlay image is Deluxe Paint format.
-
- Returns:
- Pointer to buffer if successful
- NULL if an error reading the image
-
- Notes:
- None
-
-
- ------------------------------------------------------------------------------
- int AckWrapUp(ACKENG *ae);
- where:
- ae <- Pointer to interface structure
-
- Purpose:
- Frees up memory buffers used by the ACK engine.
-
- Returns:
- 0 always
-
- Notes:
- This function MUST be called before exiting the application so that
- XMS memory is returned to the system.
-
-
- ------------------------------------------------------------------------------
- int AckSoundInitialize(int DefaultSoundDevice);
- where:
- DefaultSoundDevice <- One of the DEV_ values in ACKSND.H
-
- Purpose:
- Starts up WORX routines and determines hardware
-
- Returns:
- 0 if okay
- -1 if error initializing
-
- Notes:
- This function MUST be called before the other sound routines are used.
- Calling with DEV_NOSOUND will force all sound off.
- Calling with DEV_PCSPEAKER will force sound through the speaker even
- if a sound card is present.
-
-
- ------------------------------------------------------------------------------
- int AckPlayBackground(char *MusicFileName);
- where:
- MusicFileName <- Name of .CMF file to begin playing in background.
-
- Purpose:
- Continuously plays the music file in the background.
-
- Returns:
- 0 if okay
- -1 if error loading file
-
- Notes:
- See text in section Sound and ACK-3D for limitations with music.
-
-
- ------------------------------------------------------------------------------
- int AckLoadSound(int VocIndex,char *VocFileName);
- where:
- VocIndex <- One of the SOUND_ defines in ACKSND.H
- VocFileName <- Name of .VOC file to load.
-
- Purpose:
- Loads up a sound file for later playing.
-
- Returns:
- 0 if okay
- -1 if error loading sound
-
- Notes:
- This function will load the .VOC file if a SoundBlaster or Adlib card
- is present. If the PC speaker is specified then the routine will look for
- .PWM files (created with the VOC2PWM.EXE program). The application should
- always call with the .VOC extent.
-
-
- ------------------------------------------------------------------------------
- void AckPlaySound(int SoundIndex);
- where:
- SoundIndex <- One of SOUND_ indexes from ACKSND.H
-
- Purpose:
- Plays the specified sound effect (.VOC file)
-
- Returns:
- Nothing
-
- Notes:
- Sound file must have been previously loaded with AckLoadSound().
-
-
- ------------------------------------------------------------------------------
- void AckStopBackground(void);
- where:
- Nothing
-
- Purpose:
- Stops the current background music file (.CMF)
-
- Returns:
- Nothing
-
- Notes:
- None
-
-
- ------------------------------------------------------------------------------
- void AckSoundShutdown(void);
- where:
- Nothing
-
- Purpose:
- Closes down the WORX routines, freeing memory, etc.
-
- Returns:
- Nothing
-
- Notes:
- This routine MUST be called before exiting the application if sound is
- being used in the ACK engine.
-
-
-
-
-
- (19) Where to Begin: (EXAMPLES)
-
- Okay, now that we've covered some of the details, let's look at how an
- application begins to use the ACK engine. The first thing to do is to make
- some decisions, such as (and these may not be related to each other);
-
- 1. Will the interface structure be in global memory, or allocated memory?
-
- 2. What size of viewport will be used?
-
- 3. Will light shading be used? Will the ceiling and floor be shaded?
-
- 4. Are objects going to be manipulated by the ACK engine, or the app?
-
- 5. Will there be an overlay screen?
-
-
- Why is question 1 important? The interface structure is not small, just to
- begin with there are four 8k arrays within it to hold the map data, as well as
- object and door arrays, so if you plan on having alot of global data in the
- application then the interface structure should be allocated from the far heap.
- It doesn't really matter to the engine since a pointer to the structure is
- passed in the library calls as far data.
-
- Let's say for our example that we'll allocate the structure and hold it in
- a global data pointer within the application. We could begin with something
- like;
-
- #include "ack3d.h"
-
- ACKENG *ae; /* This is our global pointer */
-
- /* This could be a routine that the application uses to initialize things */
- int AppInitialize(void)
- {
- int result = 0;
-
- ae = malloc(sizeof(ACKENG)); /* We first get memory for the structure */
-
- if (ae == NULL) /* Whoops, we didn't get the memory */
- return(-1); /* So return an error */
-
- memset(ae,0,sizeof(ACKENG)); /* Now we clear out the entire structure */
-
- /* Perform other initialization here */
-
- return(result);
- }
-
- Snippet 1
-
-
- This code snippet does nothing more than allocate and clear the interface
- structure that will be used by the application and the ACK engine. Now we need
- to setup the size of the viewport and initialize the engine;
-
- #include "ack3d.h"
-
- #define VIEW_X 80 /* Size of the 3D viewport */
- #define VIEW_X1 240
- #define VIEW_Y 40
- #define VIEW_Y1 160
-
-
- char *MapFileName = "LEVEL1.MAP";
-
-
- int AppSetupEngine(void)
- {
- int result;
-
- ae->WinStartX = VIEW_X;
- ae->WinStartY = VIEW_Y; /* Plug in the size we want */
- ae->WinEndX = VIEW_X1; /* for our viewport */
- ae->WinEndY = VIEW_Y1;
-
- result = AckInitialize(ae); /* Then initialize the engine! */
-
- return(result); /* 0 if no error, else errorcode */
- }
-
- Snippet 2
-
-
- Code snippet 2 sets up an arbitrary viewport size (I usually load a picture
- into Deluxe Paint and write down the coordinates of where I want the 3D walls
- to be), and then calls the engine to intialize. Upon return the value of result
- will either be zero, meaning no problems, or one of the ERR_ codes listed in
- the ACK3D.H header file. If an error occurs the application should NOT continue,
- since unpredictable results WILL occur.
-
- At this point the engine has filled in quite a bit of the ACKENG structure
- with information about the viewport and the map file that was read in and
- processed. A buffer is automatically allocated for the ScreenBuffer pointer in
- the structure and the map arrays now contain the necessary wall and object
- information to build the POV. But, we can't do that just yet. First we have
- to decide on a couple more things. Lets take code snippet 2 and expand it;
-
-
- #include "ack3d.h"
-
- #define VIEW_X 80 /* Size of the 3D viewport */
- #define VIEW_X1 240
- #define VIEW_Y 40
- #define VIEW_Y1 160
-
- #define CEILING_COLOR 23
- #define FLOOR_COLOR 27
-
- #define DOORSPEED 4 /* Doors need a speed to open/close */
-
- #define PLAYER_X 390 /* Initial X coordinate */
- #define PLAYER_Y 260 /* Initial Y coordinate */
- #define PLAYER_ANGLE 480 /* Initial POV angle */
-
-
- char *MapFileName = "LEVEL1.MAP";
-
-
- int AppSetupEngine(void)
- {
- int result;
-
- ae->WinStartX = VIEW_X;
- ae->WinStartY = VIEW_Y; /* Plug in the size we want */
- ae->WinEndX = VIEW_X1; /* for our viewport */
- ae->WinEndY = VIEW_Y1;
-
-
- ae->xPlayer = PLAYER_X; /* Setup intial coordinates */
- ae->yPlayer = PLAYER_Y; /* for the POV */
- ae->PlayerAngle = PLAYER_ANGLE;
- ae->DoorSpeed = DOORSPEED; /* Set a default door speed */
-
- result = AckInitialize(ae); /* Then initialize the engine! */
-
- if (result)
- return(result); /* Error, so get out now */
-
- result = AckReadMapFile(ae,MapFileName);
- if (result)
- return(result);
-
- ae->TopColor = CEILING_COLOR; /* Setup our colors for the */
- ae->BottomColor = FLOOR_COLOR; /* background.... */
-
- ae->LightFlag = SHADING_ON; /* Yes, we want light shading */
- result = AckBuildBackground(ae); /* Build the ceiling, floor */
-
- if (result)
- return(result); /* Error, so get out now */
-
- return(result); /* 0 if no error, else errorcode */
- }
-
- Snippet 3
-
-
-
- Code snippet 3 adds to snippet 2 by setting up the background ceiling and
- floor that the ACK engine requires. In the example above we decided to shade
- the background. By using SHADING_OFF (both found in ACK3D.H) we could also
- decide to have a solid background. Note the two defines for ceiling and floor
- color, these can be any color from 0 to 255, whatever is appropriate for the
- applications.
-
- Also note that the initial coordinates and angle of the POV are setup here
- but do not need to be. These can be deferred until the call to AckBuildView()
- is made. They are done here merely for convienence.
-
-
- Okay, things are coming along nicely. Now we need to decide if an overlay
- is needed or not. Remember an overlay is only need if there will be some part
- of the full screen that will always display over the 3D walls. An example
- would be gothic pillars, or perhaps magic staffs that border the full screen
- display. Whatever the application needs to give the visual effect.
-
- An overlay is not mandatory and does induce a slight speed degradation when
- displaying the engine. It also means the function AckDisplayScreen() must be
- called to actually use the overlay (unless the application has it's own
- routine to handle it).
-
- Let's say we want an overlay, this is what we would do;
-
-
- #include "ack3d.h"
-
-
- char *PictureFile = "MYPIC.LBM";
-
-
- int AppSetupOverlay(void)
- {
- int result = 0;
- UCHAR far *OverlayPic;
-
- OverlayPic = AckReadiff(PictureFile); /* Load a Deluxe Paint picture */
-
- if (OverlayPic == NULL) /* Whoops, got a problem */
- return(-1); /* So return with an error */
-
-
- result = AckCreateOverlay(ae,&OverlayPic[4]); /* Compile the overlay */
-
- free(OverlayPic); /* Free up the picture unless */
- /* we want to use it later */
-
- return(result);
- }
-
- Snippet 4
-
-
- This example reads in a Deluxe Paint LBM file and calls the ACK engine to
- compile it. The resulting overlay sequence will be in the interface structure
- pointed to by OverlayBuffer (unless an error occurs).
-
- One change to this code snippet would be to keep the picture buffer around
- so it can be displayed on the screen. Note, only the portion which will cover
- the 3D viewport will be compiled, NOT the entire picture.
-
- The application can use the supplied LBM read routine if it wishes, or read
- in the picture on its own, as long as the picture buffer passed to the routine
- is a flat 320 by 200 (64000 byte) image that the overlay section can be snipped
- out of.
-
- The AckReadiff() routine places the width and height of the image in the
- first four bytes of the buffer, which is why &OverlayPic[4] was passed to
- the engine.
-
- Again remember, the overlay is optional and only needs to be used if part
- of the screen is going to appear over the walls.
-
-
- By now we've setup quite a bit of the information we'll need to actually
- draw a 3D view of our map, but things will look pretty bad if we try to draw
- at this point, we don't have any bitmaps yet! Let's proceed with what is needed
- to get some walls into our application;
-
-
- #include "ack3d.h"
-
-
- typedef struct {
- int Number;
- int Type;
- char *Name;
- } BMTABLE;
-
-
- BMTABLE bmTable[] = {
- 1 ,TYPE_WALL ,"swall1.bbm",
- 2 ,TYPE_WALL ,"swall2.bbm",
- 3 ,TYPE_WALL ,"swall3.bbm",
- 4 ,TYPE_WALL ,"swall4.bbm",
- 5 ,TYPE_WALL ,"swall5.bbm",
- 58 ,TYPE_WALL ,"secret.bbm",
- 59 ,TYPE_WALL ,"secret.bbm",
- 60 ,TYPE_WALL ,"sdoor.bbm",
- 61 ,TYPE_WALL ,"sside.bbm",
- 62 ,TYPE_WALL ,"sdoor.bbm",
- 1 ,TYPE_OBJECT ,"eyeball.bbm",
- 2 ,TYPE_OBJECT ,"treasure.bbm",
- -1 ,-1 ,"" /* End of table */
- };
-
-
- int AppLoadBitmaps(void)
- {
- int result;
- int i = 0;
-
- while (bmTable[i].Number != -1)
- {
- result = AckLoadBitmap(ae,
- bmTable[i].Number,
- bmTable[i].Type,
- bmTable[i].Name);
-
- if (result) /* Error during load */
- break; /* so get out now */
-
- i++; /* Next index in table */
- }
-
-
- return(result);
- }
-
- Snippet 5
-
-
- What code snippet 5 does is loop through a table and load all the bitmaps
- for this example application (the bitmap names are arbitrary). Should an error
- occur the routine exits immediately and returns the error to the caller. Note
- also that this same routine can be used to load objects as well as walls, just
- use TYPE_OBJECT instead of TYPE_WALL (defined in ACK3D.H) for the objects.
-
- Upon return from code snippet 5 we'll have all the bitmaps loaded that are
- needed to begin. If no objects are going to be used then we can proceed, but
- for example purposes let's say we have the two objects loaded in snippet 5 to
- setup. We do this by calling the function AckCreateObject() as in the following
- example;
-
-
- #include "ack3d.h"
-
-
- int AppSetupObjects(void)
- {
- int result;
- UCHAR BitmapNumbers[2];
-
- ae->ObjList[1].Dir = 0; /* Direction doesn't matter */
- ae->ObjList[1].Speed = 0; /* is a stationary object */
-
- BitmapNumbers[0] = 1; /* Bitmap to use with object (eyeball) */
-
- result = AckCreateObject(ae,1,1,BitmapNumbers);
-
- if (result) /* An error occurred */
- return(result); /* so get out now */
-
- ae->ObjList[2].Dir = 0; /* Again a direction is irrelavent */
- ae->ObjList[2].Speed = 0; /* Because speed 0 is stationary */
-
- BitmapNumbers[0] = 2; /* Bitmap to use (treasure) */
-
- result = AckCreateObject(ae,2,1,BitmapNumbers);
-
- return(result);
- }
-
- Snippet 6
-
-
- Snippet 6 is a brute force method of creating the objects. A more elegant
- method would be to setup a table to create the objects, similiar to what we
- did in snippet 5 with the bitmaps.
-
-
-
- At this point we've got the engine initialized, a background buffer built,
- bitmaps loaded, and objects created. We're not even in graphics mode yet! The
- ACK engine provides some support functions that may be used (unless the
- application has its own), to setup graphics mode and later go back to text
- mode. These functions are;
-
-
- AckSetVGAmode(); <- Sets video into mode 13h (320x200 w/ 256 colors)
-
- AckSetTextmode(); <- Sets video into mode 3 (80x25 16 color text)
-
-
- Use these if desired, they are just thrown in as support routines. Another
- routine can also be used, this one reads in a palette file and sets up the
- VGA palette registers;
-
-
- AckLoadAndSetPalette( FileName ); <- Reads a palette file and sets regs.
-
-
- This routine may be used AFTER the video is placed in graphics mode. Pass
- the name of a 768 byte palette file to use. Upon return the new palette will
- be set. Let's put these routines into a code snippet;
-
-
- #include "ack3d.h"
-
-
- char *PalFile = "DEMO.PAL";
-
-
-
- int AppSetGraphics(void)
- {
- int result;
-
-
- AckSetVGAmode(); /* Go into graphics */
-
- result = AckLoadAndSetPalette(PalFile);
-
- return(result);
- }
-
-
- Snippet 7
-
-
-
-
- Okay, we've got the beginnings of a 3D game! As part of this introduction
- we need to be concerned with only a couple more things. Beyond that it becomes
- the applications responsibility to handle user interaction with the engine.
-
- Once we're ready to begin displaying the POV on the screen we need to make
- one more mandatory call to the engine to tell it to build the current scene. The
- code snippet below shows this process (for now we'll assume the application is
- going to let the ACK engine perform the actual display);
-
-
-
- #include "ack3d.h"
-
-
- void AppShow3D(void)
- {
-
- /* Any preprocessing the application wishes to do can go here */
-
- AckBuildView(ae); /* Tell the ACK engine to construct the POV */
-
- AckDisplayScreen(ae); /* Display the POV on the video screen */
-
-
- }
-
- Snippet 8
-
-
-
- Remember that the initial coordinates of the POV were setup in code snippet
- number 3 above. They can easily be setup anytime before calling AckBuildView()
- if the application so decides.
-
-
-
-
- It's high time we put all these code snippets together into a running
- example program. This is shown below;
-
-
-
- #include "ack3d.h"
-
- #define VIEW_X 80 /* Size of the 3D viewport */
- #define VIEW_X1 240
- #define VIEW_Y 40
- #define VIEW_Y1 160
-
- #define CEILING_COLOR 23
- #define FLOOR_COLOR 27
-
-
- #define PLAYER_X 390 /* Initial X coordinate */
- #define PLAYER_Y 260 /* Initial Y coordinate */
- #define PLAYER_ANGLE 480 /* Initial POV angle */
-
-
- char *MapFileName = "LEVEL1.MAP";
- char *PictureFile = "MYPIC.LBM";
- char *PalFile = "DEMO.PAL";
-
- typedef struct {
- int Number;
- int Type;
- char *Name;
- } BMTABLE;
-
-
- BMTABLE bmTable[] = {
- 1 ,TYPE_WALL ,"swall1.bbm",
- 2 ,TYPE_WALL ,"swall2.bbm",
- 3 ,TYPE_WALL ,"swall3.bbm",
- 4 ,TYPE_WALL ,"swall4.bbm",
- 5 ,TYPE_WALL ,"swall5.bbm",
- 58 ,TYPE_WALL ,"secret.bbm",
- 59 ,TYPE_WALL ,"secret.bbm",
- 60 ,TYPE_WALL ,"sdoor.bbm",
- 61 ,TYPE_WALL ,"sside.bbm",
- 62 ,TYPE_WALL ,"sdoor.bbm",
- 1 ,TYPE_OBJECT ,"eyeball.bbm",
- 2 ,TYPE_OBJECT ,"treasure.bbm",
- -1 ,-1 ,"" /* End of table */
- };
-
-
- /* Prototypes */
- int AppInitialize(void);
- int AppSetupEngine(void);
- int AppSetupOverlay(void);
- int AppLoadBitmaps(void);
- int AppSetupObjects(void);
- int AppSetGraphics(void);
- void AppShow3D(void);
-
-
- /* Entry point for application */
-
- int main(void)
- {
- int result,done = 0;
-
- result = AppInitialize();
-
- if (result)
- {
- printf("Error initializing: ErrorCode = %d\n",result);
- return(1);
- }
-
-
- result = AppSetupEngine();
-
- if (result)
- {
- printf("Error setting up ACK engine: ErrorCode = %d\n",result);
- return(1);
- }
-
-
- result = AppSetupOverlay();
-
- if (result)
- {
- printf("Error loading overlay: ErrorCode = %d\n",result);
- return(1);
- }
-
-
- result = AppLoadBitmaps();
-
- if (result)
- {
- printf("Error loading bitmaps: ErrorCode = %d\n",result);
- return(1);
- }
-
-
- result = AppSetupObjects();
-
- if (result)
- {
- printf("Error creating objects: ErrorCode = %d\n",result);
- return(1);
- }
-
-
-
- result = AppSetGraphics();
-
- if (result)
- {
- AckSetTextmode();
- printf("Error loading palette: ErrorCode = %d\n",result);
- return(1);
- }
-
-
-
- while (!done)
- {
- AppShow3D();
-
-
- if (getch() == 27) /* Check for the escape key */
- break;
-
- }
-
-
-
- AckSetTextmode();
-
- return(0);
- }
-
-
- Example 3
-
-
-
-
- (20) Closing comments:
-
- And here I said at the start of this document that writing docs is too
- much like work! Anyway, I hope the information provided above, as well as the
- actual source to the ACK engine, is enough to get you started on your own
- 3D adventure. It's been a very exciting project and I've met alot of nice folks
- who have pitched in and helped in many ways. I wish to express thanks to all
- of you who made this possible and advanced thanks to all of you who may use the
- engine to produce more games for all of us to enjoy.
-
- Those who have been of GREAT help are;
-
- Jaimi McEntire who helped a great deal with programming and graphics.
- Jaimi also did the neat space station picture in the title!
-
- Ken Lemieux who provided the space dudes and shuttle!
-
- Steve Salter who did ALOT of the wall and object graphics!
-
- Frank Sachse who provided the sound routine interface and music!
-
- Ron Sachse who provided some of the wall bitmaps!
-
- Mark Betz who allowed me to use his fading routines!
-
- Bart Stewart who built an image editor for this thing!
-
- Michael Wilson who's XMS code was readily available and is working
- great!
-
- Thanks guys! Without you the ACK engine and demo would not have been
- possible!
-
- Now some bad news: There is currently a problem with displaying objects that
- sometimes causes "ghosts" images to be displayed. While this does not cause any
- damage to anything it is very annoying and I haven't been able to track down the
- cause. If anyone has any suggestions or ideas I'd appreciate hearing them and
- building an update for others. Thanks!
-
- One last note: This will be my final installment of ACK3D for awhile. I'm
- not going to drop out of the picture totally but I've got some new things that
- I'm eager to try out! Maybe some more goodies will come out of it, one can
- never tell.
-
- If you wish to reach me on CompuServe I'm usually hanging around the Game
- Design library of The Gamers forum, or you can email me direct. My CompuServe
- account is 72355,655.
-
- Sincerely,
- Lary Myers
-
-